package com.hexiang.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hexiang.response.CommonResponse;
import com.hexiang.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@RestController
@Slf4j
public class AsyncDeferredController {

    private final TaskService taskService;
    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;
    @Resource
    private ThreadPoolTaskExecutor taskCallback;

    @Autowired
    public AsyncDeferredController(TaskService taskService) {
        this.taskService = taskService;
    }

    @GetMapping(value = "/deferred", produces = "text/html")
    public DeferredResult<String> executeSlowTask() {
        log.info("executeSlowTaskStart");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        CompletableFuture.supplyAsync(taskService::execute, taskExecutor)
                .whenCompleteAsync((result, throwable) -> {
                    log.info("result is {}", result);
                    deferredResult.setResult(result);
                }, taskCallback);
        log.info("executeSlowTaskEnd");
        return deferredResult;
    }

    @PostMapping(value = "/deferredPost", produces = "application/json")
    public DeferredResult<CommonResponse> deferredPost(@RequestBody String jsonString) throws JsonProcessingException {
        log.info("deferredPostStart");
        String name = (String) objectMapper.readValue(jsonString, Map.class).get("name");
        DeferredResult<CommonResponse> deferredResult = new DeferredResult<>();
        CompletableFuture.supplyAsync(() -> taskService.execute(name), taskExecutor)
                .whenCompleteAsync((result, throwable) -> deferredResult.setResult(result), taskCallback);
        log.info("deferredPostEnd");
        return deferredResult;
    }

    @GetMapping(value = "/deferredTimeout", produces = "text/html")
    public DeferredResult<String> executeSlowTaskTimeout() {
        log.info("executeSlowTaskTimeoutStart");
        DeferredResult<String> deferredResult = new DeferredResult<>(1000L);
        deferredResult.onTimeout(() -> {
            log.info("异步线程执行超时");
            deferredResult.setResult("task timeout");
        });
        CompletableFuture.supplyAsync(taskService::timeout, taskExecutor)
                .whenCompleteAsync((result, throwable) -> deferredResult.setResult(result), taskCallback);
        log.info("executeSlowTaskTimeoutEnd");
        return deferredResult;
    }

    @GetMapping(value = "/deferredErr", produces = "text/html")
    public DeferredResult<String> deferredErr() {
        log.info("deferredErrStart");
        DeferredResult<String> deferredResult = new DeferredResult<>();
        CompletableFuture.supplyAsync(taskService::executeErr, taskExecutor)
                .whenCompleteAsync((result, throwable) -> deferredResult.setResult(throwable.getMessage()), taskCallback);
        log.info("deferredErrEnd");
        return deferredResult;
    }
}